/**
 * 
 */
package com.abso.mp3tunes.locker.core.data;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.dom4j.Element;

import com.abso.mp3tunes.locker.core.LockerServiceException;

/**
 * A track is a song stored on the Locker.
 */
public final class Track extends AbstractLockerData {

	/** The unique identifier. */
	private String id;

	/** The title. */
	private String title;

	/** The track number. */
	private int number;

	/** The time length (in milliseconds). */
	private double length;

	/** The original file name. */
	private String fileName;

	/** The file key assciated with the track on the Locker storage system. */
	private String fileKey;

	/** The file size (in bytes). */
	private int fileSize;

	/** The download URL. */
	private String downloadURL;

	/** The play URL. */
	private String playURL;

	/** The identifier of the album. */
	private String albumId;

	/** The title of the album. */
	private String albumTitle;

	/** The year of the album. */
	private String albumYear;

	/** The identifier of the artist. */
	private String artistId;

	/** The name of the artist. */
	private String artistName;

	/** The URL of the album art. */
	private String albumArtURL;

	/**
	 * Constructs a new track.
	 * 
	 * @param item
	 *            the &lt;item&gt; XML element.
	 */
	public Track(Element item) {
		this.id = LockerDataUtils.getString(item, "trackId");
		this.title = LockerDataUtils.getString(item, "trackTitle");
		this.number = LockerDataUtils.getInt(item, "trackNumber", -1);
		this.length = LockerDataUtils.getDouble(item, "trackLength", -1.0);
		this.number = LockerDataUtils.getInt(item, "trackNumber", -1);
		this.fileName = LockerDataUtils.getString(item, "trackFileName");
		this.fileKey = LockerDataUtils.getString(item, "trackFileKey");
		this.fileSize = LockerDataUtils.getInt(item, "trackFileSize", -1);
		this.downloadURL = LockerDataUtils.getString(item, "downloadURL");
		this.playURL = LockerDataUtils.getString(item, "playURL");
		this.albumId = LockerDataUtils.getString(item, "albumId");
		this.albumTitle = LockerDataUtils.getString(item, "albumTitle");
		this.albumYear = LockerDataUtils.getString(item, "albumYear");
		this.artistId = LockerDataUtils.getString(item, "artistId");
		this.artistName = LockerDataUtils.getString(item, "artistName");
		this.albumArtURL = LockerDataUtils.getString(item, "albumArtURL");
	}

	/**
	 * Returns the unique identifier.
	 * 
	 * @return the unique identifier.
	 */
	public String getId() {
		return id;
	}

	/**
	 * Returns the title.
	 * 
	 * @return the title.
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * Returns the number.
	 * 
	 * @return the number.
	 */
	public int getNumber() {
		return number;
	}

	/**
	 * Returns the time length (in milliseconds).
	 * 
	 * @return the time length.
	 */
	public double getLength() {
		return length;
	}

	/**
	 * Returns the original file name.
	 * 
	 * @return the original file name.
	 */
	public String getFileName() {
		return fileName;
	}

	/**
	 * Returns the file key.
	 * 
	 * @return the file key.
	 */
	public String getFileKey() {
		return fileKey;
	}

	/**
	 * Returns the file size (in bytes).
	 * 
	 * @return the file size.
	 */
	public int getFileSize() {
		return fileSize;
	}

	/**
	 * Returns the download URL.
	 * 
	 * @return the download URL.
	 */
	public String getDownloadURL() {
		return downloadURL;
	}

	/**
	 * Returns the play URL.
	 * 
	 * @return the play URL.
	 */
	public String getPlayURL() {
		return playURL;
	}

	/**
	 * Returns the album identifier.
	 * 
	 * @return the album identifier.
	 */
	public String getAlbumId() {
		return albumId;
	}

	/**
	 * Returns the album title.
	 * 
	 * @return the album title.
	 */
	public String getAlbumTitle() {
		return albumTitle;
	}

	/**
	 * Returns the album year.
	 * 
	 * @return the album year.
	 */
	public String getAlbumYear() {
		return albumYear;
	}

	/**
	 * Returns the artist identifier.
	 * 
	 * @return the artist identifier.
	 */
	public String getArtistId() {
		return artistId;
	}

	/**
	 * Returns the artist name.
	 * 
	 * @return the artist name.
	 */
	public String getArtistName() {
		return artistName;
	}

	/**
	 * Returns the album art URL.
	 * 
	 * @return the album art URL.
	 */
	public String getAlbumArtURL() {
		return albumArtURL;
	}

	/**
	 * Returns the album art as an array of bytes.
	 * 
	 * @return the album art.
	 * @throws LockerServiceException
	 *             if an error occurred retrieving the album art.
	 */
	public byte[] getAlbumArtAsBytes() throws LockerServiceException {
		HttpMethodBase httpMethod = new GetMethod(getAlbumArtURL());
		InputStream inStream = null;
		try {
			HttpClient httpClient = new HttpClient();
			httpClient.executeMethod(httpMethod);
			inStream = httpMethod.getResponseBodyAsStream();
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[4096];
            int len;
            while ((len = inStream.read(buffer)) > 0) {
                outStream.write(buffer, 0, len);
            }
            return outStream.toByteArray();
		} catch (HttpException e) {
			throw new LockerServiceException("Unable to retrieve album art", e);
		} catch (IOException e) {
			throw new LockerServiceException("Unable to retrieve album art", e);
		} finally {
			httpMethod.releaseConnection();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.MULTI_LINE_STYLE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	public int hashCode() {
		return getId().hashCode();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	public boolean equals(Object obj) {
		if (!(obj instanceof Track)) {
			return false;
		}
		return getId().equals(((Track) obj).getId());
	}

}